Overview

Velt uses Organizations, Folders, Documents, and Locations to organize data and users into the same shared spaces and subspaces, enabling granular access controls. The data hierarchy in Velt follows this structure: Organization -> Folders -> Documents -> Locations. Here we will cover each of these concepts in detail:

Organizations

An Organization is the top-level entity.
  • It contains documents, locations, and users.
  • Within an organization, you can create multiple documents, and each document can contain several locations.
Think of an organization as a Google Account belonging to a company (e.g., Company A). This account may have several users (Company A employees). A document will be any file created within the organization (e.g., document, spreadsheet, slides, etc.). A location will be any child section within the document (e.g., slide within a presentation).

Create an Organization

Client side

When the user signs into an organization as described below, if an organization is not present then it will be created automatically.

Server side

Using our REST APIs, you will be able to create an organization and also set metadata like name, description, etc.

Provision Access to an Organization

You can provision access to an organization using this

Sign in User into an Organization

  • In the identify() method, set the organizationId and also add it to the auth token.
  • If you want the user to log into another organization, call the identify method again with the new organizationId and new auth token.

Folders

Folders allow you to organize your documents in a hierarchical structure, similar to a file system. You can create folders within folders, and each folder can contain multiple documents. This helps in managing and navigating your documents efficiently.
  • Folders can contain documents and other folders.
  • Uses same permission model as Organizations and Documents.

How it works

When you subscribe to a folder, you can choose to subscribe to all the documents within that folder or only a specific set of documents. This is done using the setDocuments method, where you can pass a folderId to specify the folder you want to subscribe to. If you want to subscribe to all documents in a folder, you can set the allDocuments flag to true. This will automatically subscribe you to all the documents in the folder, and you will receive real-time updates for all of them. If you only want to subscribe to a specific set of documents within a folder, you can pass an array of document objects to the setDocuments method, along with the folderId.

Frontend API

Subscribing to a folder

You can subscribe to a folder and its documents using the setDocuments method.
Using Hooks:
const { setDocuments } = useSetDocuments();

{/* Subscribe to a folder and all its documents */}
const rootDocument = [
  {
    id: 'document-1',
    metadata: {
      documentName: 'Document 1'
    }
  }
];

setDocuments(
  rootDocument,
  {
    folderId: 'folder1',
    allDocuments: true
  }
);


{/* Subscribe to a folder and some documents */}
const documents = [
  {
    id: 'document-1',
    metadata: {
      documentName: 'Document 1'
    }
  },
  {
    id: 'document-2',
    metadata: {
      documentName: 'Document 2'
    }
  }
];

setDocuments(
  documents,
  {
    folderId: 'folder1',
  }
);
Using API:
{/* Subscribe to a folder and all its documents */}
client.setDocuments(
  rootDocument,
  {
    folderId: 'folder1',
    allDocuments: true
  }
);

{/* Subscribe to a folder and some documents */}
client.setDocuments(
  documents,
  {
    folderId: 'folder1',
  }
);

Fetching folder metadata

You can fetch folder metadata and subfolders by organizationId or folderId with pagination.
// Get all folders for a specific organization
client.fetchFolders({
  organizationId: 'org1'
});


client.fetchFolders({
  organizationId: 'org1',
  folderId: 'folder1'
});

interface FetchFoldersRequest {
  organizationId?: string;
  folderId?: string;
}

interface FetchFoldersResponse {
  data: Record<string, FolderMetadata> | null;
  nextPageToken: string;
}

Backend API

You can perform CRUD operations for folders and manage their access control using the backend API. For more details, see the REST API documentation.

Documents

A Document represents a shared collaborative space where users can interact. Documents live inside the Organization. Documents contain:
  • All feature data (e.g., Comments, Presence, Cursors, etc.).
  • Locations
  • Users: These are different from Organization Users. (more details in Access Control section)
Users logged into the same Document ID can see each other’s Presence, Cursors, Comments etc. For example, in a slide presentation application, the entire slide deck is a document.

Set a Single Document

  • Use this to initialize and subscribe to a single Document.
  • Once you set the document, you will start receiving realtime updates from the document.
  • Params:
    • documentId: The unique identifier for the document.
    • metadata: (optional) This is a key/value pair object where you can set metadata about the document such as documentName. documentName is a special field that we use to display the document name in some Velt Components.
Using Hooks:
useSetDocument('unique-document-id', {documentName: 'Document Name'});
Using API:
const { client } = useVeltClient();

useEffect(() => {
    if (client) {
        client.setDocument('unique-document-id', {documentName: 'Document Name'});
    }
}, [client]);

Set Multiple Documents

  • Use this to set and subscribe to multiple documents at the same time.
  • You can specify 30 documents at a time.
  • The first document in the list will be considered as the root document.
  • For features like comments, notifications, recorder, reactions etc. you will be able to read and write to multiple documents at the same time.
  • For features like cursors, presence, huddle, live state sync etc. it will default to the root document.
  • Sidebar will automatically show data from all the documents.
Params:
Using Hooks:
const documents = [
  {
    id: 'document-1',
    metadata: {
      documentName: 'Document 1'
    }
  },
  {
    id: 'document-2',
    metadata: {
      documentName: 'Document 2'
    }
  }
];
const { setDocuments } = useSetDocuments();
setDocuments(documents);
Using API:
const documents = [
  {
    id: 'document-1',
    metadata: {
      documentName: 'Document 1'
    }
  },
  {
    id: 'document-2',
    metadata: {
      documentName: 'Document 2'
    }
  }
];
client.setDocuments(documents);

Read/Write data from multiple documents on the same page

  • If you want to display data (eg: comments) from multiple documents on the same page, add data-velt-document-id attribute to the container that contains the document.
  • It will be used to identify which part of the DOM belongs to which document.
<div class="document-container" data-velt-document-id="document-1">
  ...
</div>

<div class="document-container" data-velt-document-id="document-2">
  ...
</div>

<div class="document-container" data-velt-document-id="document-3">
  ...
</div>

Unset a Single Document

  • Use this to unsubscribe from the root Document
  • Once you unset the document, you will no longer receive realtime updates from the document.
  • For some parts of your app, you may not need Velt. In such cases, you can unset the document.
Using Hooks:
useUnsetDocumentId();
Using API:
const { client } = useVeltClient();

useEffect(() => {
    if (client) {
        client.unsetDocumentId();
    }
}, [client]);

Unset Multiple Documents

  • Use this to unsubscribe from all documents at once.
Using Hooks:
useUnsetDocuments();
Using API:
const { client } = useVeltClient();

useEffect(() => {
    if (client) {
        client.unsetDocuments();
    }
}, [client]);

Get Document Metadata

  • Use this to get the metadata of a Document.
  • This is useful when you want to display the document name in your app or any custom metadata that you have set.
  • This returns a subscription with DocumentMetadata object.
const { client } = useVeltClient();

useEffect(() => {
    if (client) {
        client.getDocumentMetadata().subscribe((documentMetadata) => {
            console.log("Current document metadata: ", documentMetadata);
        });
    }
}, [client]);

Access Documents from Other Organizations

  • By default, users can only access documents within their own organization.
  • Enable cross-organization access by specifying the organizationId of the target document in the document metadata.
  • Ensure that the user has access to the target document in the target organization.
Using Hook:
{/* Single Document */}
useSetDocument("DOCUMENT_ID", {
  organizationId: 'ANOTHER_ORGANIZATION_ID'
});

{/* Multiple Documents */}
const documents = [
  {
    id: 'document-1',
    metadata: {
      documentName: 'Document 1'
    }
  },
  {
    id: 'document-2',
    metadata: {
      documentName: 'Document 2'
    }
  }
];
useSetDocuments(documents, {
  organizationId: 'ANOTHER_ORGANIZATION_ID'
});
Using API:
{/* Single Document */}
client.setDocument("DOCUMENT_ID", {
  organizationId: 'ANOTHER_ORGANIZATION_ID'
});

{/* Multiple Documents */}
const documents = [
  {
    id: 'document-1',
    metadata: {
      documentName: 'Document 1'
    }
  },
  {
    id: 'document-2',
    metadata: {
      documentName: 'Document 2'
    }
  }
];
client.setDocuments(documents, {
  organizationId: 'ANOTHER_ORGANIZATION_ID'
});

Locations

Locations are optional subspaces within a document, providing finer partitioning of data. For instance:
  • In a slide presentation, the entire slide presentation will be a document each individual slide will be a location.
  • In a dashboard, the entire dashboard is a document but various filters applied will be locations;
  • In a video player, the entire video will be the document and timestamps will be locations.
If a Document is like a house, a Location is like a room within the house.
A Location is a JSON object that represents a specific area or context in your application. Locations can represent:
  • Pages
  • Sections
  • Video frames
  • Data points on maps/charts
  • Any other contextual area
The location object has three special fields:
  • id (required): A unique identifier for the location that can be used to reference it later
  • locationName (recommended): A human-readable name displayed in Velt components like the VeltCommentsSideBar
  • version (optional): An object with id and name fields to track different versions of the location

Set Location

Using Hooks:
useSetLocation({
  'id': 'locationId1',
  'locationName': 'MainVideoPlayer',
  'page': 'mainPage',
  'version': {
    'id': 'v2.3',
    'name': 'Version Name'
  },
  'videoFrame': '120'
  // You can keep adding more field to make the location very specific.
  // The field names can be anything.
})
Using API:
client.setLocation({
  'id': 'locationId1',
  'locationName': 'MainVideoPlayer',
  'page': 'mainPage',
  'version': {
    'id': 'v2.3',
    'name': 'Version Name'
  },
  'videoFrame': '120'
  // You can keep adding more field to make the location very specific.
  // The field names can be anything.
})

Set Multiple Locations

There are three main steps to adding multiple locations:
  1. Add a root location
  2. Add additional locations
  3. Bind elements containers to locations
1

Add a root location

useSetDocument('some_document_id');

useSetLocation({ 
  'id': 'locationRoot',
  'locationName': 'PageWithVideo',
  'version': {
		'id': 'v1.0',
		'name': 'Version Name'
	},
})
2

Add additional locations

Add additional locations on the page by using set location with the true parameter:
useSetLocation({ 
  'id': 'locationId1',
  'locationName': 'Scene1LocationName',
  'version': {
		'id': 'v1.0',
		'name': 'Version Name'
	},
  'videoFrame': '120'
}, true)

useSetLocation({
  'id': 'locationId2',
  'locationName': 'Scene2LocationName',
  'version': {
		'id': 'v1.0',
		'name': 'Version Name'
	},
  'videoFrame': '50'
}, true)

useSetLocation({
  'id': 'locationId3',
  'locationName': 'Scene3LocationName',
  'version': {
		'id': 'v1.0',
		'name': 'Version Name'
	},
  'videoFrame': '80'
}, true)
3

Bind element containers to locations

  • When you render multiple elements representing different locations on the same page, then you can bind each element container to the correct location by adding the data-velt-location-id attribute.
  • This ensures that the comment added within the location is associated with the correct location.
<div class="page">
  <div id="location1-id" data-velt-location-id="location1-id">
    <div class="card">
      // any content
    </div>
  </div>
  <div id="location2-id" data-velt-location-id="location2-id">
    <div class="card">
      // any content
    </div>
  </div>
</div>

Unset Locations

Unset locations by ids or all of them if you don’t specify any parameters.
// remove specific locations
client.unsetLocationsIds(['location1', 'location2', 'location3'])

// remove all locations
client.unsetLocationsIds()

Users

A User represents a person who has been authenticated with the Velt SDK. Once a User has been authenticated, their profile can be seen within Velt’s collaboration features. For example in the Comments feature, the User's name is shown by their comment and in @mentions. Additionally in the Presence and in Cursors features, the User's name is shown by their avatar bubble and mouse cursors.

Contact List

When the user is on a document, they can @mention other users. By default, the contact list for the @mention feature includes users from:
  • Organization users.
  • Folder users.
  • Document users.
  • User Groups.
  • @here: This is a special group that includes only the users explicitly added on the document. This doesn’t include organization users or organization user groups.
Contact list is automatically updated:
  • as users are added or deleted from the organization, folder or the document.
  • users are added or deleted from user groups.
  • user groups are added or updated.
Access Control: Using the access control features, you can control which users are visible on the contact list on a given document.
  • Organization users can access the entire Organization contact list on a given organization document.
  • Guest (Non-organization) users who were granted access to an organization document cannot access the Organization contact list. If you want to show some organization contacts to these guest users, then you need to explicitly add them to the document.

User Groups

You can create custom collection of users such as engineering, marketing etc to group organization users and manage access effectively.
  • This can be used to @mention a team group versus just individuals (just like in Slack).
  • This can also be used to provision access to documents. (coming soon)
  • Organization users can belong to multiple organization user groups.
  • Non-organization users can’t be part of organization user groups.
Learn more:

Sign in a User

To sign in a user call the identify() method with the userId and authToken as described here.
Even though using JWT Auth token is optional, we recommend using it for additional security.
Once a User has been authenticated, their profile can be seen within Velt’s collaboration features. For example in the Comments feature, the User’s name is shown by their comment and in @mentions. Additionally in the Presence and in Cursors features, the User’s name is shown by their avatar bubble and mouse cursors.

Sign in with force reset

By default when you identify a User, we maintain the user auth in the browser unless you explicitly sign out the logged in user. If you are changing a User’s access or any metadata and want those changes to be reflected immediately, then you should re-call the identify method with forceReset option set to true. Learn more here.

Sign out a User

In a given session or browser tab, if you want to switch users, you need to first signout the current user and then sign in using identify method again. This will ensure we clean up the current user session and start a new session with the new user.
client.signOutUser();

Authentication

There are two ways to authenticate a user in Velt.
  1. Using an Auth Provider (recommended)
  2. Using Identify method

1. Use Auth Provider

In this approach, you set an auth provider with the User you want to authenticate and a method that returns a JWT token. The method will be called whenever Velt needs a token - during first sign in and when the token expires. This needs to be defined in Velt Provider during initialization.
Frontend:
<VeltProvider authProvider={{
  user,
  retryConfig: { retryCount: 3, retryDelay: 1000 },
  generateToken: async () => {
    const token = await __generateVeltAuthTokenFromYourBackend__();
    return token;
  }
}} />
Backend: Refer to Generate Token REST API

2. Use Identify with JWT Token

In this approach, you will call the identify with the user object and a JWT token. Here you are responsible for re-generating a JWT token whenever it expires. This gives you more flexibility on when and where to initialize the user and generate the token.
Using Hook:
  const user = {
    userId: uid,
    organizationId: organizationId, // this is the organization id the user belongs to. You should always use this.
    name: displayName,
    email: email,
    photoUrl: photoURL,
    color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
    textColor: textColor // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
  };

  useIdentify(user, {
    authToken: authToken, // this is optional but highly recommended.
  });
Using API:
await client.identify(user, {
    authToken: authToken, // this is optional but highly recommended.
});
Backend: Refer to Generate Token REST API

Force re-login user

By default when you identify a User, we maintain the user auth in the browser unless you explicitly sign out the logged in user. If you are changing a User’s access or any metadata and want those changes to be reflected immediately, then you should re-call the identify method with forceReset option set to true. Default: false
await client.identify(user, {
  forceReset: true
});

Access Control

There are two ways to manage access control of Velt features and data:
  1. On Demand Access Control: Your app may have a very complex and granular access control system in place. Instead of mapping that into Velt, this allows you to have complete flexibility with very little setup. Instead of pre-syncing users and running a ongoing sync job, you can grant temporary time based access or permanent access to the resource. Whenever the user logs in and accesses a resource in your app, you can use our API to grant or revoke access to Velt features and data on the fly.
  2. Synced Access Control: Use this if you have a traditional hierarchical access control system like Google Drive. This requires slightly more setup.
  • Initial syncing: Once you set up Velt, you can do a one time sync of your existing users in Velt.
  • Ongoing syncing: When a user is added or removed in your app, you will also need to add or remove them from Velt.

1. On Demand Access Control

Types

  • Temporary access: Access is granted for a specific time period.
  • Permanent access: Access is granted permanently until you explicitly revoke it.

During login

Generate a JWT token with the user’s information and the resource id. Learn more
Frontend:
<VeltProvider authProvider={{
  user,
  retryConfig: { retryCount: 3, retryDelay: 1000 },
  generateToken: async () => {
    const token = await __generateVeltAuthTokenFromYourBackend__();
    return token;
  }
}} />
Backend: Refer to Generate Token REST API

When User switches between resources

a. Add permissions: Refer to Add Permissions REST API b. Remove permissions: Refer to Remove Permissions REST API

2. Synced Access Control

Initial syncing

You can add users in bulk using Add Users REST API

During login

When you use identify API and have auto-create organization users enabled in console, the user will be added to the organization after successful login.

Ongoing syncing

When a user is added or removed in your app, you will also need to add or remove them from Velt using the:

Access Control Types

These can be applied to Folder and Document resources.
  • public: default Any user who successfully initializes the given Velt resource can access the resource data whether or not they are part of the resource.
  • organizationPrivate: All users in the same organization as the resource can access the resource.
  • restricted: Only users explicitly added to the resource will have access.
Changing access control types:
  • Changing default: You can change the default access control using the console.
  • Changing access control for a specific resource:
    • Folder: You can change the access control using the REST API.
    • Document: You can change the access control using the REST API.

Resources on which access control can be applied

  1. Organization
  2. Folder
  3. Document
1. Organization
  • A user can be added to multiple organizations but can only log in to one organization at a time.
  • By default, all organization users have access to all organization resources including folders, documents, locations and user contacts.
  • Access to resources can be restricted by setting controls at the individual resource level.
2. Folder
  • A user can be added to multiple Folders but can only initialize one Folder at a time.
  • By default, all Folder users have access to all Folder resources including sub folders, documents, locations and user contacts.
  • Access to individual resources within the Folder cannot be restricted by setting controls at the individual resource level.
  • Access of the Folder cascades to all resources within the Folder.
3. Document
  • A user can be added to multiple Documents and can initialize multiple documents at once.
  • Document inherits the access control of the Organization and Folder it is in.
  • You can set Document level access control to override the Organization’s access control it inherited but not the Folder’s access control.